package dpkg

import (
	"fmt"
	"strings"

	"gitee.com/liumou_site/gbm"
	"gitee.com/liumou_site/logger"
)

// InstallListLocal 安装本地软件包列表
// 该函数接收一个文件列表和一个名称，将文件列表转换为字符串后调用InstallFile进行安装
// 参数:
//
//	fileList []string: 待安装的本地软件包文件列表
//	name string: 软件包的名称
func (api *DpkgStruct) InstallListLocal(fileList []string, name string) {
	// 将文件列表转换为单个字符串，其中文件名之间用空格分隔
	installStr := gbm.SliceToString(fileList, " ")
	// 调用InstallFile函数安装转换后的软件包字符串
	api.InstallFile(installStr, name)
}

// InstallFile 安装指定的软件包文件。
// 参数:
//
//	pac: 软件包文件的路径。
//	name: 软件包的名称，用于日志显示。
func (api *DpkgStruct) InstallFile(pac string, name string) {
	// 初始化安装结果状态为 false。
	api.Result = false
	// 将当前对象的忽略设置继承给 Sudo 对象。
	api.Sudo.Ignore = api.Ignore
	// 将当前对象的黑洞设置继承给 Sudo 对象。
	api.Sudo.BlackHole = api.BlackHole
	// 构造 dpkg 命令以安装软件包。
	c := fmt.Sprintf("dpkg -i %s", pac)
	// 如果启用了调试模式，则记录调试日志。
	if api.Debug {
		logger.Debug(c)
	}
	// 以超级用户权限执行构造的命令。
	api.Sudo.RunSudo(c)
	// 获取执行命令后的错误信息。
	api.Err = api.Sudo.Err
	// 根据是否有错误来设置安装结果和记录日志。
	if api.Err == nil {
		api.Result = true
		// 如果启用了信息模式，则记录安装成功的日志。
		if api.Info {
			logger.Info("[ %s ] Installation succeeded", name)
		}
	} else {
		// 记录安装失败的错误日志。
		logger.Error("[ %s ] Installation failed", name)
	}
}

// UninstallSlice 卸载软件包列表中的软件包。
//
// 参数:
//
//	pacList: 需要卸载的软件包名称列表。
func (api *DpkgStruct) UninstallSlice(pacList []string) {
	// 将软件包列表拼接成一个由空格分隔的字符串。
	uninstallStr := gbm.SliceToString(pacList, " ")
	// 调用 Uninstall 方法执行卸载操作。
	api.Uninstall(uninstallStr)
}

// Uninstall 方法用于卸载指定的软件包。
// 它通过调用 dpkg 命令来实现软件包的完全卸载。
// 参数:
//
//	Package: 需要卸载的软件包名称。
func (api *DpkgStruct) Uninstall(Package string) {
	// 初始化卸载结果状态为 false
	api.Result = false
	// 执行卸载命令，使用 Sudo 运行 dpkg -P Package
	api.Sudo.RunSudo("dpkg", "-P", Package)
	// 检查卸载命令是否执行成功
	if api.Sudo.Err == nil {
		// 如果没有错误，将卸载结果状态设置为 true
		api.Result = true
		// 如果需要输出信息，则输出卸载成功的日志信息
		if api.Info {
			logger.Info("[ %s ] Uninstall succeeded", Package)
		}
	} else {
		// 如果有错误，输出卸载失败的日志信息
		logger.Error("[ %s ] Uninstall Failed", Package)
	}
	// 将 Sudo 操作的错误信息保存到 api 的 Err 字段中
	api.Err = api.Sudo.Err
}

// GetPackageStatus 使用 Dpkg查询包状态, 通过res返回字典,通过status返回查询状态,字典key(status/Name/version)
func (api *DpkgStruct) GetPackageStatus(pacPackage string) (m map[string]string) {
	cmd := fmt.Sprintf("dpkg -l | grep %s | sed -n 1p | awk '{print $1,$2,$3}'", pacPackage)
	api.Sudo.RunScript(cmd)
	m = map[string]string{
		"status":  "Query failed",
		"Name":    "Query failed",
		"version": "Query failed",
	}
	api.Err = api.Sudo.Err
	if api.Err == nil {
		strSp := strings.Split(api.Sudo.Strings, " ")
		strSp = gbm.SliceRemoveNull(strSp)
		if len(strSp) < 3 {
			return nil
		}
		fmt.Println(len(strSp))
		_name := fmt.Sprintf(strSp[1])
		_status := fmt.Sprintf(strSp[0])
		_ver := fmt.Sprintf(strSp[2])
		_ver = strings.Fields(_ver)[0]
		// 定义一个字典用于存储数据
		m = map[string]string{
			"status":  _status,
			"Name":    _name,
			"version": _ver,
		}
		if api.Info {
			logger.Info("query was successful: %s", pacPackage)
		}
	}
	return
}

// Installed 检查指定的软件包是否已安装
// 参数:
//
//	pac: 要检查的软件包名称
//
// 返回值:
//
//	bool: 如果软件包已安装，则返回true；否则返回false
func (api *DpkgStruct) Installed(pac string) bool {
	// 获取指定包的状态
	m := api.GetPackageStatus(pac)
	// 如果没有错误
	if api.Err == nil {
		// 获取状态
		status := m["status"]
		// 如果状态为已安装
		if status == "ii" {
			// 返回true
			return true
		}
	}
	// 否则返回false
	return false
}

// CheckVersion 检查指定软件包的版本是否与给定版本一致。
// 参数:
//
//	pac: 软件包的名称。
//	version: 需要检查的软件包版本。
//
// 返回值:
//
//	status_: 布尔值，指示软件包版本是否与给定版本一致。
//	ver_: 字符串，表示软件包的实际版本。
func (api *DpkgStruct) CheckVersion(pac string, version string) (status_ bool, ver_ string) {
	// 获取软件包的状态信息。
	info := api.GetPackageStatus(pac)
	// 从状态信息中提取版本号和名称。
	ver := info["version"]
	name := info["Name"]
	status := info["status"]

	// 检查是否有错误发生。
	if api.Err == nil {
		// 检查软件包名称是否匹配。
		if name != pac {
			mess := fmt.Sprintf("Name mismatch: %s != %s", name, pac)
			logger.Warn(mess)
			return false, name
		}
		// 检查软件包状态是否正常。
		if status == "ii" {
			if api.Info {
				logger.Info("Normal status")
			}
			// 检查版本号是否一致。
			if ver == version {
				if api.Info {
					logger.Info("Version consistency")
				}
				return true, ver
			} else {
				logger.Warn("Inconsistent version: %s != %s", ver, version)
				return false, ver
			}
		} else {
			logger.Warn("Abnormal status")
			return false, ver
		}
	}
	return false, ver
}

// CheckPacKey 检查系统中是否存在指定的软件包键
// 该方法通过运行shell命令'dpkg -l'来列出已安装的软件包，并搜索pac1和pac2关键词
// 如果找到匹配的软件包，返回true和软件包的名称；如果没有找到，返回false和空字符串
// 参数:
//
//	pac1 - 要检查的第一个软件包关键词
//	pac2 - 要检查的第二个软件包关键词
//
// 返回值:
//
//	result - 布尔值，表示是否找到了匹配的软件包
//	pac - 字符串，如果找到匹配的软件包，则返回软件包的名称
func (api *DpkgStruct) CheckPacKey(pac1, pac2 string) (result bool, pac string) {
	// 运行'dpkg -l'命令列出已安装的软件包
	api.Sudo.RunShell("dpkg -l") // 判断是否存在
	// 搜索pac1和pac2关键词，并尝试获取第一行的输出
	api.Sudo.Grep(pac1).Grep(pac2).Line(1)
	// 将输出拆分为字符串数组
	sp := strings.Fields(api.Sudo.Strings)
	// 检查数组长度是否足够，以确保我们可以安全地访问索引1
	if len(sp) >= 5 {
		// 如果长度足够，说明找到了匹配的软件包
		result = true
		// 返回匹配的软件包名称
		pac = sp[1]
	} else {
		// 如果长度不足，说明没有找到匹配的软件包
		result = false
	}
	// 返回结果和软件包名称（如果找到）
	return result, pac
}

// ConfigureAll 配置所有未初始化的软件包。
// 该方法使用sudo执行dpkg命令来配置所有未初始化的软件包。
// 如果配置成功，它会在日志中记录"Configure succeeded"；
// 如果配置失败，则记录"Configure Failed"。
// 最后，它将sudo操作的错误（如果有）赋值给api的Err属性。
func (api *DpkgStruct) ConfigureAll() {
	// 使用sudo执行dpkg命令配置所有软件包
	api.Sudo.RunSudo("dpkg --configure -a")

	// 检查sudo操作是否有错误
	if api.Sudo.Err == nil {
		// 如果没有错误，记录成功日志
		logger.Info("Configure succeeded")
	} else {
		// 如果有错误，记录失败日志
		logger.Error("Configure Failed")
	}

	// 将sudo操作的错误赋值给api的Err属性
	api.Err = api.Sudo.Err
}
